﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using S7.Net;
using TianYi.Helper;

namespace TianYi
{
    public class PlcController
    {
    //创建消息委托
    public delegate void AddOpcLogHandler(string source, string log);
        public static event AddOpcLogHandler AddOpcLogEventHandler;
        //西门子参数
        private CpuType _cpu;
        private string _plcIp;
        private short _plcRack;
        private short _plcSlot;
        private Plc  siemensPlc;
        private bool FlagConnec;
        /// <summary>
        /// 实例化西门子OPC
        /// </summary>
        /// <param name="plcIp">ip地址</param>
        /// <param name="cpuType">cpu类型</param>
        /// <param name="plcRack">机架号</param>
        /// <param name="plcSlot">槽位</param>
        public PlcController(string cpu, string ip, short rack, short slot)
        {
            this._plcIp = ip;
            this._cpu = TransferCputype(cpu);
            this._plcRack = rack;
            this._plcSlot = slot;
            siemensPlc = new Plc(_cpu, ip, rack, slot);
        }
        /// <summary>
        /// cpu型号选择
        /// </summary>
        /// <param name="cputype"></param>
        /// <returns></returns>
        private CpuType TransferCputype(string cputype)
        {
            switch (cputype.ToLower())
            {
                case "s7-200":
                    return CpuType.S7200;
                case "s7-300":
                    return CpuType.S7300;
                case "s7-400":
                    return CpuType.S7400;
                case "s7-1200":
                    return CpuType.S71200;
                case "s7-1500":
                    return CpuType.S71500;
                default:
                    break;
            }
            return CpuType.S7200;
        }

      
        #region Methods
        /// <summary>
        /// 连接PLC
        /// </summary>
        public bool Connect()
        {
            try
            {
                siemensPlc.Open();
                if (siemensPlc.IsConnected)
                {
                    FlagConnec = true;
                }
                else
                {
                    FlagConnec = false;
                }

            }
            catch (Exception ex)
            {
                LogHelper.WriteError("plc连接失败!");
            }
            return FlagConnec;
        }
        /// <summary>
        /// 断开PLC
        /// </summary>
        private void DisConnect()
        {
            try
            {
                siemensPlc.Close();
            }
            catch (Exception ex)
            {

                MessageBox.Show("断开PLC时出现异常，错误代码：" + ex.Message);
            }
        }
        public bool IsConnected
        {
            get
            {
                return siemensPlc.IsConnected;
            }
        }
        /// <summary>
        /// 开启自动读取/写入服务
        /// </summary>
        public void StartService()
        {
            Connect();//连接plc
            Task.Factory.StartNew(new Action(() =>
            {
                while (true)
                {
                    if (!siemensPlc.IsConnected)
                    {
                        //如果没连接 则进行重连
                        Connect();
                    }

                }
            }), TaskCreationOptions.LongRunning);
        }

        #region bool的读写
        /// <summary>
        /// 写入bit值
        /// </summary>
        /// <param name="data">字节数组</param>
        /// <param name="index">bit位</param>
        /// <param name="value">值</param>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public byte SetbitValue(byte data, int index, bool value)
        {
            if (index > 7 || index < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            int v = 1 << index;
            return value ? (byte)(data | v) : (byte)(data & ~v);
        }
        /// <summary>
        /// 读取bit
        /// </summary>
        /// <param name="data">字节数组</param>
        /// <param name="index">bit位</param>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public bool GetBitValue(byte data, int index)
        {
            if (index > 7 || index < 0)
            {
                throw new ArgumentOutOfRangeException();
            }
            bool bit = (data & (1 << index)) != 0;
            return bit;
        }
        #endregion

        #region 写入Byte数组
        /// <summary>
        /// 改变Byte中数据
        /// </summary>
        /// <param name="buffers">字节数组</param>
        /// <param name="arrIndex">偏移量</param>
        /// <param name="type"></param>
        /// <param name="value"></param>
        public void ChangeByteData(byte[] buffers, int arrIndex, object value)
        {
            if (value is short)
            {
                byte[] int16Byte = BitConverter.GetBytes(Convert.ToInt16(value));
                Array.Reverse(int16Byte);
                Buffer.BlockCopy(int16Byte, 0, buffers, arrIndex, int16Byte.Length);
            }
            else if (value is int)
            {
                byte[] int32Byte = BitConverter.GetBytes(Convert.ToInt32(value));
                Array.Reverse(int32Byte);
                Buffer.BlockCopy(int32Byte, 0, buffers, arrIndex, int32Byte.Length);
            }
            else if (value is double)
            {
                byte[] doubleByte = BitConverter.GetBytes(Convert.ToDouble(value));
                Array.Reverse(doubleByte);
                Buffer.BlockCopy(doubleByte, 0, buffers, arrIndex, doubleByte.Length);
            }
            else if (value is float)
            {
                byte[] floatByte = BitConverter.GetBytes(Convert.ToSingle(value));
                Array.Reverse(floatByte);
                Buffer.BlockCopy(floatByte, 0, buffers, arrIndex, floatByte.Length);
            }
            else if (value is string)
            {
                string str = Convert.ToString(value);
                byte[] stringByte = Encoding.Default.GetBytes(str);
                Buffer.BlockCopy(stringByte, 0, buffers, arrIndex, stringByte.Length);
            }
        }
        #endregion

        #region 从Byte数组中获取数据
        /// <summary>
        /// 从byte数组获取Int16的数据
        /// </summary>
        /// <param name="buffers">byte数组</param>
        /// <param name="arrIndex">索引</param>
        /// <returns></returns>
        public short GetInt16DataByBytes(byte[] buffers, int arrIndex)
        {
            byte[] buffer = buffers.Skip(arrIndex).Take(2).ToArray().Reverse().ToArray();
            return BitConverter.ToInt16(buffer, 0);
        }
        /// <summary>
        /// 从byte数组获取Int32的数据
        /// </summary>
        /// <param name="buffers">byte数组</param>
        /// <param name="arrIndex">索引</param>
        /// <returns></returns>
        public int GetInt32DataByBytes(byte[] buffers, int arrIndex)
        {
            byte[] buffer = buffers.Skip(arrIndex).Take(4).ToArray().Reverse().ToArray();
            return BitConverter.ToInt32(buffer, 0);
        }
        /// <summary>
        /// 从byte数组获取double的数据
        /// </summary>
        /// <param name="buffers">byte数组</param>
        /// <param name="arrIndex">索引</param>
        /// <returns></returns>
        public double GetDoubleDataByBytes(byte[] buffers, int arrIndex)
        {
            byte[] buffer = buffers.Skip(arrIndex).Take(8).ToArray().Reverse().ToArray();
            return BitConverter.ToDouble(buffer, 0);
        }
        /// <summary>
        /// 从byte数组获取float的数据
        /// </summary>
        /// <param name="buffers">byte数组</param>
        /// <param name="arrIndex">索引</param>
        /// <returns></returns>
        public float GetFloatDataByBytes(byte[] buffers, int arrIndex)
        {
            byte[] buffer = buffers.Skip(arrIndex).Take(4).ToArray().Reverse().ToArray();
            return BitConverter.ToSingle(buffer, 0);
        }
        /// <summary>
        /// 从byte数组获取string的数据
        /// </summary>
        /// <param name="buffers">byte数组</param>
        /// <param name="startIndex">起始位置</param>
        /// <param name="startIndex">字符长度</param>
        /// <returns></returns>
        public string GetStringDataByBytes(byte[] buffers, int startIndex, int length)
        {
            byte[] strLength = buffers.Skip(startIndex).Take(2).ToArray();
            byte byteLength = strLength[1];
            int validLength = Convert.ToInt16(byteLength);
            byte[] buffer = buffers.Skip(startIndex + 2).Take(validLength).ToArray();
            return Encoding.Default.GetString(buffer);
        }
        /// <summary>
        /// 将接收的字节处理转换为string字节输出
        /// </summary>
        /// <param name="dataByte"></param>
        /// <returns></returns>
        public string GetPlcDataString(byte[] dataByte, int length)
        {
            try
            {
                if (Convert.ToInt32(dataByte[1]) > 0)
                {
                    string result = string.Empty;
                    byte[] buffer = new byte[length - 2];
                    for (int i = 2; i < length; i++)
                    {
                        buffer[i - 2] = dataByte[i];
                    }
                    result = Encoding.ASCII.GetString(buffer);
                    return result;
                }
                return "";
            }
            catch
            {

                throw;
            }
        }
        #endregion

        /// <summary>
        /// 读取字符串数据
        /// </summary>
        /// <param name="address">变量地址</param>
        /// <returns>字符串</returns>
        public string ReadString(string address)
        {
            try
            {
                string res = siemensPlc.Read(address).ToString();
                return res;
            }
            catch
            {
                return "";
            }
        }
        /// <summary>
        /// 读取字符串类型
        /// </summary>
        /// <param name="db">db地址</param>
        /// <param name="startByteAdr">起始偏移量</param>
        /// <param name="varCount">变量大小</param>
        /// <returns></returns>
        public string ReadString(int db, int startByteAdr, int varCount)
        {
            try
            {
                string res = siemensPlc.Read(DataType.DataBlock, db, startByteAdr, VarType.String, varCount).ToString();
                return res;
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return "";

            }
        }
        /// <summary>
        /// 读取WORD、INT类型数据
        /// </summary>
        /// <param name="address">变量地址</param>
        /// <returns>整形数据</returns>
        public int ReadInt(string address)
        {
            try
            {
                int res = Convert.ToInt16(siemensPlc.Read(address));
                return res;
            }
            catch
            {
                return -9999;
            }
        }
        /// <summary>
        /// 读取小数类型
        /// </summary>
        /// <param name="address">变量地址</param>
        /// <returns>小数数据</returns>
        public double ReadDouble(string address)
        {
            try
            {
                 
                double res = Convert.ToDouble(siemensPlc.Read(address));
                return res;
            }
            catch
            {
                return -999.999;
            }
        }

        /// <summary>
        /// 读取Lreal类型
        /// </summary>
        /// <param name="dbBlock">所选择的数据库块</param>
        /// <param name="startByteAdr">数据偏移量</param>
        /// <param name="dataType">数据块类型</param>
        /// <param name="varCount">数据长度</param>
        /// <returns></returns>
        public double ReadLReal(int dbBlock, int startByteAdr,int varCount)
        {
            try
            {
                var result = siemensPlc.Read(DataType.DataBlock, dbBlock, startByteAdr, VarType.LReal, varCount);
                return (double)result;
            }
            catch 
            {

                return -999.999;
            }
           
        }

        /// <summary>
        /// 读取小数类型
        /// </summary>
        /// <param name="db">db地址</param>
        /// <param name="startByteAdr">起始偏移量</param>
        /// <param name="varCount">变量大小 Double类型默认为4</param>
        /// <returns></returns>
        public double ReadDouble(int db, int startByteAdr, int varCount)
        {
            try
            {
                
                double res =Math.Round(Convert.ToDouble(siemensPlc.Read(DataType.DataBlock, db, startByteAdr, VarType.Real, varCount)),3)  ;
                
                return res;
            }
            catch
            {
                return -999.999;
            }
        }
        /// <summary>
        /// 读取布尔类型
        /// </summary>
        /// <param name="address">变量地址</param>
        /// <returns>布尔类型</returns>

        public bool ReadBool(int db, int startByteAdr, int varCount)
        {
            try
            {
                bool res = Convert.ToBoolean(siemensPlc.Read(DataType.DataBlock, db, startByteAdr, VarType.Bit, varCount));
                return res;

            }
            catch
            {
                return false;
            }
        }


        public void WriteString(string address, string value)
        {
            try
            {
                siemensPlc.Write(address, value);
            }
            catch
            {

            }
        }
        public void WriteInt(string address, short value)
        {
            try
            {
                siemensPlc.Write(address, (short)value);
            }
            catch
            {

            }
        }
        public void WriteDouble(string address, double value)
        {
            try
            {
                siemensPlc.Write(address, value);
            }
            catch
            {

            }
        }
        public void WriteBool(string address, bool value)
        {
            try
            {
                siemensPlc.Write(address, value);
            }
            catch
            {

            }
        }
        public void Close()
        {
            try
            {
                siemensPlc.Close();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        public byte[] ReadBytes(DataType dt, int db, int startAdr, int count)
        {
            try
            {

                byte[] results = siemensPlc.ReadBytes(dt, db, startAdr, count);
                return results;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
            return null;
        }
        #endregion
    }
}
